using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace Library.IniFiles
{
    public class IniFileWriter : IDisposable
    {
        private string _currentSection;
        private FileInfo _file;
        private StreamWriter _writer;
        private bool _isFirstLine = true;

        public IniFileWriter(FileInfo file) 
        {
            Open(file);
        }

        public void Open(FileInfo file) 
        {
            _file = file;
            Open();
        }

        public void Open()
        {
            if (!_file.Directory.Exists)
            {
                Directory.CreateDirectory(_file.Directory.FullName);
            }
            _writer = new StreamWriter(_file.OpenWrite());
            _isFirstLine = true;
        }

        public void Close()
        {
            if (_writer != null)
            {
                _writer.Flush();
                _writer.Close();
                _writer.Dispose();
                _writer = null;
            }
        }

        public void Write(string section, string key, string value)
        {
            WriteSection(section);
            WriteKey(key, value);
        }

        public void WriteComment(string comment)
        {
            string[] lines = comment.Replace("\r", "").Split('\n');
            foreach (string line in lines)
            {
                _writer.Write(";");
                _writer.WriteLine(line);
            }
        }

        public void WriteSection(string section)
        {
            if ((_currentSection ?? string.Empty) != (section ?? string.Empty))
            {
                if (!_isFirstLine) _writer.WriteLine();

                _writer.Write("[");
                _writer.Write(section);
                _writer.WriteLine("]");

                _currentSection = section;
                _isFirstLine = false;
            }
        }

        public void WriteKey(string key, params string[] values)
        {
            _writer.Write(key);
            _writer.Write("=");

            bool first = true;
            foreach (string val in values)
            {
                if (string.IsNullOrEmpty(val)) continue;
                if (!first)
                {
                    _writer.Write(", ");
                }

                int indexOfAnyBreakingChars = val.IndexOfAny(new char[] { '"', '\r', '\n', ',', ';' });
                if (indexOfAnyBreakingChars >= 0)
                {
                    _writer.Write("\"");
                    _writer.Write(val.Replace("\"", "\"\""));
                    _writer.Write("\"");
                }
                else
                {
                    _writer.Write(val);
                }
                first = false;
            }
            _writer.WriteLine(string.Empty);
            _isFirstLine = false;

        }

        public static void Write(string fileName, IniFile iniFile)
        {
            Write(fileName, iniFile, false);
        }

        public static void Write(StreamWriter writer, IniFile iniFile)
        {
            Write(writer, iniFile, false);
        }

        public static void Write(string fileName, IniFile iniFile, bool writeEmptySections)
        {
            FileInfo finf = new FileInfo(fileName);
            Write(finf.CreateText(), iniFile, writeEmptySections);
        }

        public static void Write(StreamWriter writer, IniFile iniFile, bool writeEmptySections)
        {            
            try 
            {
                bool isFirstSectionHeading = true;
                foreach (IniSection section in iniFile.Sections.Values)
                {
                    try
                    {
                        if (!section.IsHeaderSection)
                        {
                            if (isFirstSectionHeading)
                            {
                                isFirstSectionHeading = false;
                                if (iniFile.HasHeaderSection) writer.WriteLine();
                            }
                            else writer.WriteLine();

                            if (section.KeyValuesCollection != null)
                            {
                                if ((section.KeyValuesCollection.Count > 0) || writeEmptySections)
                                {
                                    writer.WriteLine("[" + section.Title + "]");
                                }
                            }
                        }

                        foreach (string key in section.KeyValuesCollection.Keys)
                        {
                            try
                            {
                                writer.Write(key + "=");
                                bool first = true;
                                List<string> vals = section.KeyValuesCollection[key];
                                foreach (string val in vals)
                                {
                                    if (string.IsNullOrEmpty(val)) continue;
                                    if (!first)
                                    {
                                        writer.Write(", ");
                                    }

                                    int indexOfAnyBreakingChars = val.IndexOfAny(new char[] { '"', '\r', '\n', ',', ';' });
                                    if (indexOfAnyBreakingChars >= 0)
                                    {
                                        writer.Write("\"");
                                        writer.Write(val.Replace("\"", "\"\""));
                                        writer.Write("\"");
                                    }
                                    else
                                    {
                                        writer.Write(val);
                                    }
                                    first = false;
                                }
                                writer.WriteLine();
                            }
                            catch (Exception ex)
                            {
                                writer.WriteLine();
                                writer.WriteLine(";EXCEPTION=" + ex.Message);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        writer.WriteLine();
                        writer.WriteLine(";EXCEPTION=" + ex.Message);
                    }
                }
            }
            catch (Exception ex) 
            {
                System.Console.Out.WriteLine(ex.Message);
                System.Console.Out.WriteLine(ex.StackTrace);
            }
            finally
            {
                writer.Flush();
                writer.Close();
                writer = null;
            }
        }

        public void Dispose()
        {
            Close();
        }
    }
}
